﻿Imports Ocean.OceanFramework.MVVM
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.Threading
Imports BusinessEntityObjects
Imports BusinessLayer

Public Class ApplicationMainWindowViewModel
    Inherits ClosableViewModelBase

#Region " Declarations "

    Private _bolLoadingRecentBlogActivity As Boolean = False
    Private _cmdLoadEditContactsAjaxComboBoxSelectionCommand As ICommand
    Private _cmdLoadEditContactsItemsControlSelectionCommand As ICommand
    Private _cmdRecentBlogActivityCommand As ICommand
    Private _cmdRefreshActivityDataCommand As ICommand
    Private _enumLogoVisibility As Visibility = Visibility.Visible
    Private _intProgress As Integer
    Private _objRecentBlogActivity As New List(Of BlogPost)
    Private _objTimeZoneLookupDictionary As Dictionary(Of String, String)
    Private WithEvents _objBackgroundWorker As BackgroundWorker
    Private WithEvents _objForms As New ObservableCollection(Of ContainerFormViewModelBase)

#End Region

#Region " Properties "

    Public ReadOnly Property Forms() As ObservableCollection(Of ContainerFormViewModelBase)
        Get
            Return _objForms
        End Get
    End Property

    Public ReadOnly Property LoadingActivityVisibility() As Visibility
        Get

            If _bolLoadingRecentBlogActivity Then
                Return Visibility.Visible

            Else
                Return Visibility.Collapsed
            End If

        End Get
    End Property

    Public Property LogoVisibility() As Visibility
        Get
            Return _enumLogoVisibility
        End Get

        Private Set(ByVal Value As Visibility)
            _enumLogoVisibility = Value
            OnPropertyChanged("LogoVisibility")
        End Set
    End Property

    Public Property Progress() As Integer
        Get
            Return _intProgress
        End Get

        Private Set(ByVal Value As Integer)
            _intProgress = Value
            OnPropertyChanged("Progress")
        End Set
    End Property

#End Region

#Region " Command Properties "

    Public ReadOnly Property LoadEditContactsAjaxComboBoxSelectionCommand() As ICommand
        Get

            If _cmdLoadEditContactsAjaxComboBoxSelectionCommand Is Nothing Then
                _cmdLoadEditContactsAjaxComboBoxSelectionCommand = New RelayCommand(AddressOf Me.LoadEditContactsAjaxComboBoxSelectionExecute)
            End If

            Return _cmdLoadEditContactsAjaxComboBoxSelectionCommand
        End Get
    End Property

    Public ReadOnly Property LoadEditContactsItemsControlSelectionCommand() As ICommand
        Get

            If _cmdLoadEditContactsItemsControlSelectionCommand Is Nothing Then
                _cmdLoadEditContactsItemsControlSelectionCommand = New RelayCommand(AddressOf Me.LoadEditContactsItemsControlSelectionExecute)
            End If

            Return _cmdLoadEditContactsItemsControlSelectionCommand
        End Get
    End Property

    Public ReadOnly Property RecentBlogActivityCommand() As ICommand
        Get

            If _cmdRecentBlogActivityCommand Is Nothing Then
                _cmdRecentBlogActivityCommand = New RelayCommand(AddressOf RecentBlogActivityExecute, AddressOf CanRecentBlogActivityExecute)
            End If

            Return _cmdRecentBlogActivityCommand
        End Get
    End Property

    Public ReadOnly Property RefreshActivityDataCommand() As ICommand
        Get

            If _cmdRefreshActivityDataCommand Is Nothing Then
                _cmdRefreshActivityDataCommand = New RelayCommand(AddressOf RefreshActivityDataExecute, AddressOf CanRefreshActivityDataExecute)
            End If

            Return _cmdRefreshActivityDataCommand
        End Get
    End Property

#End Region

#Region " Constructor "

    Public Sub New()
        _objTimeZoneLookupDictionary = MakeLookUpDictionary()
        MyBase.ViewModelFriendlyName = "Simple M-V-VM for WPF LOB"

        If My.Computer.Network.IsAvailable Then
            LoadRecentBlogPosts()
        End If

    End Sub

#End Region

#Region " Command Methods "

    Public Function CanRecentBlogActivityExecute(ByVal param As Object) As Boolean
        Return Not _bolLoadingRecentBlogActivity AndAlso _objRecentBlogActivity.Count > 0
    End Function

    Public Function CanRefreshActivityDataExecute(ByVal param As Object) As Boolean
        Return Not _bolLoadingRecentBlogActivity AndAlso My.Computer.Network.IsAvailable
    End Function

    Public Sub LoadEditContactsAjaxComboBoxSelectionExecute(ByVal param As Object)
        LoadForm(Of EditContactsAjaxComboBoxSelectionViewModel)()
    End Sub

    Public Sub LoadEditContactsItemsControlSelectionExecute(ByVal param As Object)
        LoadForm(Of EditContactItemsControlSelectionViewModel)()
    End Sub

    Public Sub RecentBlogActivityExecute(ByVal param As Object)

        Dim objForm As ContainerFormViewModelBase = Me.Forms.FirstOrDefault(Function(i) GetType(RecentActivityViewModel) Is i.GetType)

        If objForm Is Nothing Then
            LoadViewModel(New RecentActivityViewModel(_objRecentBlogActivity))

        Else
            DisplayFormInTabControl(objForm)
        End If

    End Sub

    Public Sub RefreshActivityDataExecute(ByVal param As Object)
        LoadRecentBlogPosts()
    End Sub

    Private Sub LoadForm(Of T As ContainerFormViewModelBase)()

        Dim objForm As ContainerFormViewModelBase = Me.Forms.FirstOrDefault(Function(i) GetType(T) Is i.GetType)

        If objForm Is Nothing Then
            LoadViewModel(Activator.CreateInstance(Of T))
            'LoadViewModel(DirectCast(Activator.CreateInstance(GetType(T)), ContainerFormViewModelBase))
        Else

            If objForm.IsSingleInstanceForm Then
                DisplayFormInTabControl(objForm)

            Else
                LoadViewModel(Activator.CreateInstance(Of T))
                'LoadViewModel(DirectCast(Activator.CreateInstance(GetType(T)), ContainerFormViewModelBase))
            End If

        End If

    End Sub

#End Region

#Region " Methods "

    Public Function CanApplicationClose() As Boolean

        For Each obj As ContainerFormViewModelBase In Me.Forms
            DisplayFormInTabControl(obj)

            If obj.VerifyDataBeforeClosing = False Then
                Return False
            End If

        Next

        Return True
    End Function

    Private Sub DisplayFormInTabControl(ByVal obj As ContainerFormViewModelBase)

        Dim objCVS As ICollectionView = CollectionViewSource.GetDefaultView(_objForms)

        If objCVS IsNot Nothing Then
            objCVS.MoveCurrentTo(obj)
        End If

    End Sub

    Private Sub Forms_CollectionChanged(ByVal sender As Object, ByVal e As System.Collections.Specialized.NotifyCollectionChangedEventArgs) Handles _objForms.CollectionChanged

        If _objForms.Count = 0 Then

            If _enumLogoVisibility <> Visibility.Visible Then
                Me.LogoVisibility = Visibility.Visible
            End If

        Else

            If _enumLogoVisibility <> Visibility.Collapsed Then
                Me.LogoVisibility = Visibility.Collapsed
            End If

        End If

        If e.Action = Specialized.NotifyCollectionChangedAction.Add Then

            If e.NewItems IsNot Nothing Then

                For Each obj As ContainerFormViewModelBase In e.NewItems
                    AddHandler obj.RequestClose, AddressOf OnChildFormRequestClose
                Next

            End If

        End If

        If e.Action = Specialized.NotifyCollectionChangedAction.Remove Then

            If e.OldItems IsNot Nothing Then

                For Each obj As ContainerFormViewModelBase In e.OldItems
                    RemoveHandler obj.RequestClose, AddressOf OnChildFormRequestClose
                Next

            End If

        End If

    End Sub

    Private Sub LoadViewModel(ByVal obj As ContainerFormViewModelBase)
        _objForms.Add(obj)
        DisplayFormInTabControl(obj)
    End Sub

    Private Sub OnChildFormRequestClose(ByVal sender As Object, ByVal e As EventArgs)

        Dim obj As ContainerFormViewModelBase = TryCast(sender, ContainerFormViewModelBase)

        If obj IsNot Nothing Then
            _objForms.Remove(obj)
            obj = Nothing
        End If

    End Sub

#End Region

#Region " Recent Blog Post Methods "

    Private Sub _objBackgroundWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles _objBackgroundWorker.DoWork

        Dim intTotalContacts As Integer = ContactBLL.CreateInstance.Count
        Dim objRecentBlogActivity As New List(Of BlogPost)
        Dim intX As Integer

        For Each obj As ContactFeed In ContactBLL.CreateInstance.GetFeeds
            intX += 1
            _objBackgroundWorker.ReportProgress(CType(((intX / intTotalContacts) * 100), Integer), obj.Author)

            Try

                For Each objBlogPost As BlogPost In RetrieveFeeds(obj.Author, obj.FeedURL)
                    objRecentBlogActivity.Add(objBlogPost)
                Next

            Catch ex As Exception
                ex.Data.Add("ContactFeed", obj)
                'This was removed to prevent exceptions being throw during training day.
                'Throw ex
            End Try

        Next

        _objBackgroundWorker.ReportProgress(100)
        e.Result = objRecentBlogActivity
    End Sub

    Private Sub _objBackgroundWorker_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles _objBackgroundWorker.ProgressChanged
        Me.Progress = e.ProgressPercentage

        If e.UserState IsNot Nothing Then
            MyBase.ViewModelFriendlyName = String.Format("Loading recent blog posts for {0}", e.UserState.ToString)
        End If

    End Sub

    Private Sub _objBackgroundWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles _objBackgroundWorker.RunWorkerCompleted
        Me.Progress = 100
        _bolLoadingRecentBlogActivity = False
        MyBase.ViewModelFriendlyName = "Simple M-V-VM for WPF LOB"
        OnPropertyChanged("LoadingActivityVisibility")

        If e.Error Is Nothing Then
            _objRecentBlogActivity = CType(e.Result, List(Of BlogPost))
            _objRecentBlogActivity.Sort()

        Else

            If TypeOf e.Error Is System.Net.WebException Then

                Dim obj As ContactFeed = CType(e.Error.Data("ContactFeed"), ContactFeed)
                ViewModelUIService.ExceptionDialog(e.Error, "Read Feed Exception", "Web Exception While Reading Feed Verify Data", String.Format("Author:{0},  Feed URL:{1}", obj.Author, obj.FeedURL))

            Else

                If e.Error.Data.Contains("ContactFeed") Then

                    Dim obj As ContactFeed = CType(e.Error.Data("ContactFeed"), ContactFeed)
                    ViewModelUIService.ExceptionDialog(e.Error, "Exception", "Exception While Loading Recent Blog Posts, Veryify Data", String.Format("Author:{0},  Feed URL:{1}, Exception Message:{2}", obj.Author, obj.FeedURL, e.Error.Message))

                Else
                    ViewModelUIService.ExceptionDialog(e.Error, "Exception", "Exception While Loading Recent Blog Posts")
                End If

            End If

        End If

    End Sub

    'Special characters in RSS Feeds are automatically cleaned by LINQ-to-Xml but often they
    'are double encoded, so this method filters feeds again by replacing
    'some of the special characters with corresponding text.
    Private Function EscapeXml(ByVal strDirtyXml As String) As String
        strDirtyXml = strDirtyXml.Replace("&amp;", "&")
        strDirtyXml = strDirtyXml.Replace("&lt:", "<")
        strDirtyXml = strDirtyXml.Replace("&gt", ">")
        strDirtyXml = strDirtyXml.Replace("&quot;", """")
        strDirtyXml = strDirtyXml.Replace("&apos;", "'")
        strDirtyXml = strDirtyXml.Replace("&ograve;", "ò")
        strDirtyXml = strDirtyXml.Replace("&egrave;", "è")
        strDirtyXml = strDirtyXml.Replace("&ugrave;", "ù")
        strDirtyXml = strDirtyXml.Replace("&agrave;", "à")
        strDirtyXml = strDirtyXml.Replace("&hellip;", "...")
        Return strDirtyXml
    End Function

    Private Sub LoadRecentBlogPosts()

        If Not _bolLoadingRecentBlogActivity Then
            _bolLoadingRecentBlogActivity = True
            OnPropertyChanged("LoadingActivityVisibility")
            _objBackgroundWorker = New BackgroundWorker
            _objBackgroundWorker.WorkerReportsProgress = True
            _objBackgroundWorker.RunWorkerAsync()
        End If

    End Sub

    Private Function MakeLookUpDictionary() As Dictionary(Of String, String)

        'This code is here because http://feeds2.feedburner.com/Pixel-In-Gene uses the old Internet Date Format by including PDT and PST in the date time.
        Dim obj As New Dictionary(Of String, String)
        obj.Add("ACDT", "+1030") ' Australian Central Daylight
        obj.Add("ACST", "+0930") ' Australian Central Standard
        obj.Add("ADT", "-0300") ' (US) Atlantic Daylight
        obj.Add("AEDT", "+1100") ' Australian East Daylight
        obj.Add("AEST", "+1000") ' Australian East Standard
        obj.Add("AHDT", "-0900")
        obj.Add("AHST", "-1000")
        obj.Add("AST", "-0400") ' (US) Atlantic Standard
        obj.Add("AT", "-0200") ' Azores
        obj.Add("AWDT", "+0900") ' Australian West Daylight 
        obj.Add("AWST", "+0800") ' Australian West Standard 
        obj.Add("BAT", "+0300") ' Bhagdad 
        obj.Add("BDST", "+0200") ' British Double Summer 
        obj.Add("BET", "-1100") ' Bering Standard 
        obj.Add("BST", "-0300") ' Brazil Standard 
        obj.Add("BT", "+0300") ' Baghdad 
        obj.Add("BZT2", "-0300") ' Brazil Zone 2 
        obj.Add("CADT", "+1030") ' Central Australian Daylight 
        obj.Add("CAST", "+0930") ' Central Australian Standard 
        obj.Add("CAT", "-1000") ' Central Alaska 
        obj.Add("CCT", "+0800") ' China Coast 
        obj.Add("CDT", "-0500") ' (US) Central Daylight 
        obj.Add("CED", "+0200") ' Central European Daylight 
        obj.Add("CET", "+0100") ' Central European 
        obj.Add("CST", "-0600") ' (US) Central Standard 
        obj.Add("EAST", "+1000") ' Eastern Australian Standard 
        obj.Add("EDT", "-0400") ' (US) Eastern Daylight 
        obj.Add("EED", "+0300") ' Eastern European Daylight 
        obj.Add("EET", "+0200") ' Eastern Europe 
        obj.Add("EEST", "+0300") ' Eastern Europe Summer 
        obj.Add("EST", "-0500") ' (US) Eastern Standard 
        obj.Add("FST", "+0200") ' French Summer 
        obj.Add("FWT", "+0100") ' French Winter 
        obj.Add("GMT", "-0000") ' Greenwich Mean 
        obj.Add("GST", "+1000") ' Guam Standard 
        obj.Add("HDT", "-0900") ' Hawaii Daylight 
        obj.Add("HST", "-1000") ' Hawaii Standard 
        obj.Add("IDLE", "+1200") ' Internation Date Line East 
        obj.Add("IDLW", "-1200") ' Internation Date Line West 
        obj.Add("IST", "+0530") ' Indian Standard 
        obj.Add("IT", "+0330") ' Iran 
        obj.Add("JST", "+0900") ' Japan Standard 
        obj.Add("JT", "+0700") ' Java 
        obj.Add("MDT", "-0600") ' (US) Mountain Daylight 
        obj.Add("MED", "+0200") ' Middle European Daylight 
        obj.Add("MET", "+0100") ' Middle European 
        obj.Add("MEST", "+0200") ' Middle European Summer 
        obj.Add("MEWT", "+0100") ' Middle European Winter 
        obj.Add("MST", "-0700") ' (US) Mountain Standard 
        obj.Add("MT", "+0800") ' Moluccas 
        obj.Add("NDT", "-0230") ' Newfoundland Daylight 
        obj.Add("NFT", "-0330") ' Newfoundland 
        obj.Add("NT", "-1100") ' Nome 
        obj.Add("NST", "+0630") ' North Sumatra 
        obj.Add("NZ", "+1100") ' New Zealand  
        obj.Add("NZST", "+1200") ' New Zealand Standard 
        obj.Add("NZDT", "+1300") ' New Zealand Daylight 
        obj.Add("NZT", "+1200") ' New Zealand 
        obj.Add("PDT", "-0700") ' (US) Pacific Daylight 
        obj.Add("PST", "-0800") ' (US) Pacific Standard 
        obj.Add("ROK", "+0900") ' Republic of Korea 
        obj.Add("SAD", "+1000") ' South Australia Daylight 
        obj.Add("SAST", "+0900") ' South Australia Standard 
        obj.Add("SAT", "+0900") ' South Australia Standard 
        obj.Add("SDT", "+1000") ' South Australia Daylight 
        obj.Add("SST", "+0200") ' Swedish Summer 
        obj.Add("SWT", "+0100") ' Swedish Winter 
        obj.Add("USZ3", "+0400") ' USSR Zone 3 
        obj.Add("USZ4", "+0500") ' USSR Zone 4 
        obj.Add("USZ5", "+0600") ' USSR Zone 5 
        obj.Add("USZ6", "+0700") ' USSR Zone 6 
        obj.Add("UT", "-0000") ' Universal Coordinated 
        obj.Add("UTC", "-0000") ' Universal Coordinated 
        obj.Add("UZ10", "+1100") ' USSR Zone 10 
        obj.Add("WAT", "-0100") ' West Africa 
        obj.Add("WET", "-0000") ' West European 
        obj.Add("WST", "+0800") ' West Australian Standard 
        obj.Add("YDT", "-0800") ' Yukon Daylight 
        obj.Add("YST", "-0900") ' Yukon Standard 
        obj.Add("ZP4", "+0400") ' USSR Zone 3 
        obj.Add("ZP5", "+0500") ' USSR Zone 4 
        obj.Add("ZP6", "+0600") ' USSR Zone 5
        Return obj
    End Function

    Private Function ParseDate(ByVal strDateIn As String) As DateTime

        Dim datOut As DateTime

        If Date.TryParse(strDateIn, datOut) Then
            Return datOut
        End If

        Dim strKey As String = strDateIn.Substring(strDateIn.LastIndexOf(" ")).Trim

        If _objTimeZoneLookupDictionary.ContainsKey(strKey) Then
            strDateIn = strDateIn.Replace(strKey, _objTimeZoneLookupDictionary(strKey))

            If Date.TryParse(strDateIn, datOut) Then
                Return datOut
            End If

        End If

        Throw New InvalidCastException("Unable to Parse the following Date: " & strDateIn)
    End Function

    Private Function RetrieveFeeds(ByVal strAuthor As String, ByVal strFeedURL As String) As IEnumerable(Of BlogPost)

        Dim datCutoff As DateTime = Date.Today.AddDays(-15)
        Dim doc As XDocument = XDocument.Load(strFeedURL)
        Dim query = From item In doc...<item> Let datPublished = Me.ParseDate(item.<pubDate>.Value) Let strTitle = item.<title>.Value Let strURL = item.<link>.Value Let strDescription = item.<description>.Value Let strCategory = item.<category>.Value Order By datPublished Descending Where datPublished > datCutoff Select New BlogPost With {.Author = strAuthor, .Category = strCategory, .DatePublished = datPublished, .Title = EscapeXml(strTitle), .URL = strURL}
        Return query
    End Function

#End Region

End Class
